﻿using Baidu.AI.Helper;
using Microsoft.Win32;
using Microsoft.Office.Interop.Excel;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shell;
using System.Windows.Threading;
using System.Xml;
using EnergyStyle;
using System.Windows.Data;
using Action = System.Action;
using static EnergyStyle.EnergyStyleMessageBox;
using System.Windows.Input;


namespace InvoiceHelper
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : System.Windows.Window
    {
        private enum ProcessState
        {
            wait = 0,
            processing = 1,
            success = 2,
            fail = 3,
            error = 4
        }

        private enum ProcessResult
        {
            success = 0,
            fail = 1,
            error = 2
        }

        private class Invoice : INotifyPropertyChanged
        {
            public string FilePath { get; set; }
            public string FileName { get; set; }
            public string InvoiceCode { get; set; }
            public string InvoiceNum { get; set; }
            public string InvoiceType { get; set; }
            public bool Visible { get; set; }

            public string InvoiceDate
            {
                get
                {
                    return _InvoiceDate;
                }
                set
                {
                    _InvoiceDate = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(InvoiceDate)));
                }
            }
            public string _InvoiceDate;

            public string SellerName
            {
                get
                {
                    return _SellerName;
                }
                set
                {
                    _SellerName = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SellerName)));
                }
            }
            public string _SellerName;
            public string PurchaserName
            {
                get
                {
                    return _PurchaserName;
                }
                set
                {
                    _PurchaserName = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PurchaserName)));
                }
            }
            public string _PurchaserName;
            public double Amount
            {
                get
                {
                    return _Amount;
                }
                set
                {
                    _Amount = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Amount)));
                }
            }
            public double _Amount;
            public string Name
            {
                get
                {
                    return _Name;
                }
                set
                {
                    _Name = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
                }
            }
            private string _Name;
            public ProcessState ProcessState
            {
                get
                {
                    return _ProcessState;
                }
                set
                {
                    _ProcessState = value;
                    switch (value)
                    {
                        case ProcessState.wait:
                            StateIcon = new BitmapImage(new Uri("/ico/File.png", UriKind.Relative));
                            break;
                        case ProcessState.processing:
                            StateIcon = new BitmapImage(new Uri("/ico/Loading.png", UriKind.Relative));
                            break;
                        case ProcessState.success:
                            StateIcon = new BitmapImage(new Uri("/ico/Success.png", UriKind.Relative));
                            break;
                        case ProcessState.fail:
                            StateIcon = new BitmapImage(new Uri("/ico/Fail.png", UriKind.Relative));
                            break;
                        case ProcessState.error:
                            StateIcon = new BitmapImage(new Uri("/ico/error.png", UriKind.Relative));
                            break;
                    }
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ProcessState)));
                }
            }
            private ProcessState _ProcessState;
            public ImageSource StateIcon
            {
                get
                {
                    return _StateIcon;
                }
                set
                {
                    _StateIcon = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StateIcon)));
                }
            }
            private ImageSource _StateIcon;
            public Invoice(string path)
            {
                FileName = Path.GetFileName(path);
                _Name = FileName;
                FilePath = path;
                ProcessState = ProcessState.wait;
                _StateIcon = new BitmapImage(new Uri("/ico/File.png", UriKind.Relative));

                InvoiceCode = "";
                InvoiceType = "";
                InvoiceNum = "";
                _SellerName = "";
                _PurchaserName = "";
                _InvoiceDate = "";
                Visible = true;
            }

            public event PropertyChangedEventHandler? PropertyChanged;
        }

        private class BaiduCloudOcrEngine(string API_KEY, string SECRET_KEY, MainWindow window)
        {
            private readonly BaiduAI engine = new(API_KEY, SECRET_KEY);
            private readonly MainWindow Main_window = window;
            public bool Avaliable
            {
                get
                {
                    return _avaliable;
                }
            }

            private readonly bool _avaliable = true;

            public ProcessResult OCR(Invoice invoice, int OcrRate)
            {
                Stopwatch sw = Stopwatch.StartNew();
                Main_window.Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate ()
               {
                   invoice.ProcessState = ProcessState.processing;
               });

                JObject result = engine.OcrInvoice(invoice.FilePath).Result;
                while (sw.ElapsedMilliseconds < OcrRate) ;

                if (result["error_code"] == null)
                {
                    try
                    {
                        invoice.InvoiceType = ((string?)result["words_result"]?["InvoiceType"]) ?? "";
                        invoice.InvoiceCode = ((string?)result["words_result"]?["InvoiceCode"]) ?? "";
                        invoice.InvoiceNum = ((string?)result["words_result"]?["InvoiceNum"]) ?? "";
                        invoice.SellerName = ((string?)result["words_result"]?["SellerName"]) ?? "";
                        invoice.PurchaserName = ((string?)result["words_result"]?["PurchaserName"]) ?? "";
                        invoice.InvoiceDate = ((string?)result["words_result"]?["InvoiceDate"]) ?? "";
                        invoice.Amount = ((double?)result["words_result"]?["AmountInFiguers"]) ?? 0.0;

                        Main_window.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate ()
                        {
                            invoice.ProcessState = ProcessState.success;
                            invoice.Name = "[" + invoice.InvoiceType + "]  " + invoice.InvoiceCode + "-" + invoice.InvoiceNum + " (" + invoice.FileName + ")";
                        });
                        return ProcessResult.success;
                    }
                    catch (Exception)
                    {
                        Main_window.Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate ()
                        {
                            invoice.ProcessState = ProcessState.fail;
                        });
                        return ProcessResult.fail;
                    }
                }
                else if ((string?)result["error_code"] == "1616001")
                {
                    Main_window.Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate ()
                    {
                        invoice.ProcessState = ProcessState.error;
                    });
                    return ProcessResult.error;
                }
                else
                {
                    Main_window.Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate ()
                    {
                        invoice.ProcessState = ProcessState.fail;
                    });
                    return ProcessResult.fail;
                }
            }
        }

        readonly ObservableCollection<Invoice> InvoiceList;
        readonly ICollectionView InvoiceListView;
        public MainWindow()
        {
            InvoiceList = [];
            InvoiceListView = CollectionViewSource.GetDefaultView(InvoiceList);
            InvoiceListView.Filter = InvoiceListViewFilter;

            InitializeComponent();
            ListBox_Invoice.ItemsSource = InvoiceListView;
            if (!File.Exists(@"Setting.config"))
            {
                File.Copy("Template.config", "Setting.config", true);
            }
        }

        private bool InvoiceListViewFilter(object item)
        {
            Invoice invoice = (Invoice)item;
            if (invoice.Visible)
                return true;
            else
                return false;
        }

        private void Btn_Import_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new()
            {
                Multiselect = true,
                Filter = "发票文件|*.pdf;*PDF;*.png;*.PNG;*.jpg;*.JPG;*.ofd;*.OFD|pdf文件|*.pdf;*.PDF|图像文件|*.png;*PNG;*jpg;*.JPG|OFD文件|*.ofd;*.OFD"
            };
            openFileDialog.FileOk += OpenInvoiceDialog_FileOk;
            openFileDialog.ShowDialog();
        }

        private void OpenInvoiceDialog_FileOk(object? sender, CancelEventArgs e)
        {
            if (sender != null && ((OpenFileDialog)sender).FileNames.Length > 0)
            {
                foreach (string FileName in ((OpenFileDialog)sender).FileNames)
                {
                    InvoiceList.Add(new Invoice(FileName));
                }
            }
        }

        private void Btn_OCR_Click(object sender, RoutedEventArgs e)
        {
            Btn_OCR.IsEnabled = false;

            XmlDocument config = new();
            config.Load("Setting.config");

            string? API_KEY = config.SelectSingleNode("/Config/BaiduCloudOCR/APIKey")?.InnerText;
            string? SECERT_KEY = config.SelectSingleNode("/Config/BaiduCloudOCR/SecertKey")?.InnerText;
            int rate = int.Parse(config.SelectSingleNode("/Config/BaiduCloudOCR/OcrRate")?.InnerText ?? "200");

            List<Invoice> WaitOcrInvoiceList = [];

            //如果所选的发票项为空，则提示是否选择列表中的所有发票

            if (ListBox_Invoice.SelectedItems.Count == 0)
                if (EnergyStyleMessageBox.Show("是否分析列表中的所有发票", "分析", new Settings()
                {
                    Mode = Modes.ConfirmAndCancel
                }) == Result.Confirm)
                    ListBox_Invoice.SelectAll();

            // MessageBox.Show("是否分析列表中的所有发票", "提示", MessageBoxButton.YesNo) == MessageBoxResult.Yes
            foreach (Invoice invoice in ListBox_Invoice.SelectedItems)
            {
                if (invoice.ProcessState == ProcessState.wait || invoice.ProcessState == ProcessState.error)
                    WaitOcrInvoiceList.Add(invoice);
            }

            try
            {
                BaiduCloudOcrEngine engine = new(API_KEY ?? "", SECERT_KEY ?? "", this);
                progressBar.Visibility = Visibility.Visible;
                progressBar.Maximum = WaitOcrInvoiceList.Count;
                progressBar.Value = 0;
                TaskbarItemInfo.ProgressValue = 0;
                TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Normal;
                string MessageString = "开始分析";
                int Count, CurrentIndex = 1, SuccessCount = 0, ErrorCount = 0, FailCount = 0;
                Count = WaitOcrInvoiceList.Count;

                Task OcrTask = new(() =>
                       {
                           foreach (Invoice invoice in WaitOcrInvoiceList)
                           {
                               Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate ()
                                {
                                    Label_Message.Content = MessageString;
                                    progressBar.Value++;
                                    TaskbarItemInfo.ProgressValue = CurrentIndex / (double)Count;
                                    ListBox_Invoice.ScrollIntoView(invoice);
                                });
                               ProcessResult result = engine.OCR(invoice, rate);
                               switch (result)
                               {
                                   case ProcessResult.success:
                                       SuccessCount++;
                                       break;
                                   case ProcessResult.error:
                                       ErrorCount++;
                                       break;
                                   case ProcessResult.fail:
                                       FailCount++;
                                       break;
                               }
                               CurrentIndex++;
                               MessageString = "分析中 " + CurrentIndex.ToString() + "/" + Count.ToString() + " (成功: " + SuccessCount.ToString() + " 错误: " + FailCount.ToString() + " 失败: " + ErrorCount.ToString() + ")";
                               if (ErrorCount > 0)
                                   MessageString += " 部分文件分析失败,可稍后重试";
                           }
                           Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate ()
                            {
                                Btn_OCR.IsEnabled = true;
                                progressBar.Visibility = Visibility.Hidden;
                                TaskbarItemInfo.ProgressState = TaskbarItemProgressState.None;
                                MessageString = "分析完成  (成功: " + SuccessCount.ToString() + " 错误: " + FailCount.ToString() + " 失败: " + ErrorCount.ToString() + ")";
                                if (ErrorCount > 0)
                                    MessageString += " 部分文件分析失败,可稍后重试";
                                Label_Message.Content = MessageString;
                            });
                           Thread.Sleep(200);
                           Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate ()
                           {
                               TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Normal;
                           });
                           Thread.Sleep(300);
                           Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate ()
                           {
                               TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Indeterminate;
                           });
                           Thread.Sleep(200);
                           Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate ()
                           {
                               TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Normal;
                           });
                           Thread.Sleep(300);
                           Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate ()
                           {
                               TaskbarItemInfo.ProgressState = TaskbarItemProgressState.None;
                           });
                       });
                OcrTask.Start();
            }
            catch (Exception ex)
            {
                Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate ()
                {
                    Label_Message.Content = "BaiduOcr引擎初始化失败(" + ex.Message + ")";
                });
                Btn_OCR.IsEnabled = true;
            }
        }

        private void Btn_Setting_Click(object sender, RoutedEventArgs e)
        {
            Process.Start("notepad.exe", @"Setting.config");
        }

        private void Btn_SelectAll_Click(object sender, RoutedEventArgs e)
        {
            ListBox_Invoice.SelectAll();
        }

        private void Btn_DeleteAll_Click(object sender, RoutedEventArgs e)
        {
            InvoiceList.Clear();
        }

        private void Btn_Delete_Click(object sender, RoutedEventArgs e)
        {
            List<Invoice> invoices = new();
            foreach (Invoice invoice in ListBox_Invoice.SelectedItems)
            {
                invoices.Add(invoice);
            }
            foreach (Invoice invoice in invoices)
            {
                InvoiceList.Remove(invoice);
            }
        }

        private void Btn_DeleteError_Click(object sender, RoutedEventArgs e)
        {
            List<Invoice> invoices = [];
            foreach (Invoice invoice in InvoiceList)
            {
                if (invoice.ProcessState == ProcessState.fail)
                    invoices.Add(invoice);
            }
            foreach (Invoice invoice in invoices)
            {
                InvoiceList.Remove(invoice);
            }
            Label_Message.Content = "清除了" + invoices.Count + "个错误文件";
        }

        private void Btn_DeleteRepeat_Click(object sender, RoutedEventArgs e)
        {
            List<Invoice> invoices = [];
            foreach (Invoice invoice in InvoiceList)
            {
                int InvoiceIndex = InvoiceList.IndexOf(invoice);
                for (int i = InvoiceIndex + 1; i < InvoiceList.Count; i++)
                {
                    if (invoice.ProcessState == ProcessState.success && invoice.InvoiceNum == InvoiceList[i].InvoiceNum)
                    {
                        invoices.Add(invoice);
                        break;
                    }
                }
            }
            foreach (Invoice invoice in invoices)
            {
                InvoiceList.Remove(invoice);
            }
            Label_Message.Content = "清除了" + invoices.Count + "个重复发票文件";
        }

        private void SelectPurchaserName(object sender, RoutedEventArgs e)
        {
            string? PurchaserName = ((System.Windows.Controls.Label)sender).Content.ToString();
            if (EnergyStyleMessageBox.Show("筛选购买方为 " + PurchaserName, "筛选") == EnergyStyleMessageBox.Result.Confirm)
            {
                foreach (Invoice invoice in InvoiceList)
                {
                    if (invoice.PurchaserName != PurchaserName)
                        invoice.Visible = false;
                }
                InvoiceListView.Refresh();
                Label_Message.Content = "已筛选购买方为 " + PurchaserName + " 的发票";
                Btn_FilterCancel.Visibility = Visibility.Visible;
            }
            /*
    MessageBoxResult ApplyFilterResult = MessageBox.Show("筛选购买方为 " + PurchaserName, "筛选", MessageBoxButton.OKCancel);
    if (ApplyFilterResult == MessageBoxResult.OK)
    {
        foreach (Invoice invoice in InvoiceList)
        {
            if (invoice.PurchaserName != PurchaserName)
                invoice.Visible = false;
        }
        InvoiceListView.Refresh();
        Label_Message.Content = "已筛选购买方为 " + PurchaserName + " 的发票";
        Btn_FilterCancel.Visibility = Visibility.Visible;
    }*/
        }

        private void Btn_Export_Click(object sender, RoutedEventArgs e)
        {
            List<Invoice> WaitExportInvoices = [];

            foreach (Invoice invoice in ListBox_Invoice.SelectedItems)
            {
                if (invoice.ProcessState == ProcessState.success)
                    WaitExportInvoices.Add(invoice);
            }

            EnergyStyleFolderDialog FolderDialog = new()
            {
                Owner = this
            };
            FolderDialog.ShowDialog();

            Task ExportTask = new(() =>
            {
                Label_Message.Dispatcher.Invoke(() =>
                  {
                      Btn_Export.IsEnabled = false;
                      Label_Message.Content = "导出中";
                  });
                Microsoft.Office.Interop.Excel.Application ExcelObject = new();

                Workbooks ExcelWorkbooks = ExcelObject.Workbooks;
                Workbook ExcelWorkbook = ExcelWorkbooks.Add(XlWBATemplate.xlWBATWorksheet); ;
                Worksheet ExcelWorksheet = (Worksheet)ExcelWorkbook.Worksheets[1];

                if (ExcelObject != null)
                {
                    ExcelWorksheet.Cells[1, 1] = "开票时间";
                    ExcelWorksheet.Cells[1, 2] = "发票代码";
                    ExcelWorksheet.Cells[1, 3] = "发票号码";
                    ExcelWorksheet.Cells[1, 4] = "销售方";
                    ExcelWorksheet.Cells[1, 5] = "购买方";
                    ExcelWorksheet.Cells[1, 6] = "金额";

                    (ExcelWorksheet.Columns[1]).Columns.ColumnWidth = 15;
                    (ExcelWorksheet.Columns[2]).Columns.ColumnWidth = 14;
                    (ExcelWorksheet.Columns[3]).Columns.ColumnWidth = 10;
                    (ExcelWorksheet.Columns[4]).Columns.ColumnWidth = 30;
                    (ExcelWorksheet.Columns[5]).Columns.ColumnWidth = 30;
                    (ExcelWorksheet.Columns[6]).Columns.ColumnWidth = 10;
                }

                try
                {
                    int index = 2;
                    foreach (Invoice invoice in WaitExportInvoices)
                    {
                        File.Copy(invoice.FilePath, FolderDialog.FolderPath + invoice.InvoiceCode + "-" + invoice.InvoiceNum + " " + invoice.SellerName + Path.GetExtension(invoice.FilePath), true);
                        if (ExcelObject != null)
                        {
                            ExcelWorksheet.Cells[index, 1] = invoice.InvoiceDate;
                            ExcelWorksheet.Cells[index, 2] = "\'" + invoice.InvoiceCode;
                            ExcelWorksheet.Cells[index, 3] = "\'" + invoice.InvoiceNum;
                            ExcelWorksheet.Cells[index, 4] = invoice.SellerName;
                            ExcelWorksheet.Cells[index, 5] = invoice.PurchaserName;
                            ExcelWorksheet.Cells[index, 6] = invoice.Amount;
                        }
                        index++;
                    }

                    if (ExcelObject != null)
                    {
                        ExcelWorkbook.Saved = true;
                        ExcelWorkbook.SaveCopyAs(FolderDialog.FolderPath + "导出发票列表.xlsx");
                        ExcelObject?.Quit();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("导出失败(" + ex.Message + ")", "导出未完成", MessageBoxButton.OK, MessageBoxImage.Error);
                }

                Label_Message.Dispatcher.Invoke(() =>
                {
                    if (WaitExportInvoices.Count != ListBox_Invoice.SelectedItems.Count)
                        MessageBox.Show("所选部分发票文件未成功导出(未进行分析或分析错误)");
                    Label_Message.Content = "导出完成";
                    Btn_Export.IsEnabled = true;
                });
            });
            if (FolderDialog.FolderPath != "" && ListBox_Invoice.SelectedItems.Count > 0)
                ExportTask.Start();
        }

        private void Btn_FilterCancel_Click(object sender, RoutedEventArgs e)
        {
            foreach (Invoice invoice in InvoiceList)
                invoice.Visible = true;
            InvoiceListView.Refresh();
            Btn_FilterCancel.Visibility = Visibility.Hidden;
        }
        private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        private void CloseWindowExecute(object sender, ExecutedRoutedEventArgs e)
        {
            Close();
        }
        private void RestoreWindowExecute(object sender, ExecutedRoutedEventArgs e)
        {
            WindowState = WindowState.Normal;
        }
        private void MinWindowExecute(object sender, ExecutedRoutedEventArgs e)
        {
            WindowState = WindowState.Minimized;
        }
        private void MaxWindowExecute(object sender, ExecutedRoutedEventArgs e)
        {
            WindowState = WindowState.Maximized;
        }
    }
}
